[レポート][BOA311-OF] 良き隣人であること: モダンアプリケーションの世界におけるレート制限 #reinvent
はじめに
こんにちは、CX事業本部、re:Invent 2022 現地参加組の田中孝明です。
セッション概要
BOA311-OF: Being good neighbors: Rate limiting in a modern application world
モダンアプリケーションを開発する際に、不注意による DDoS 攻撃やリソース不足によって他のシステムに影響を与えないようにするにはどうすればよいでしょうか?完璧な世界では、モダンアプリケーションのアーキテクチャとスケーリング機能は利点に過ぎませんが、新しい課題をもたらします。モダンアプリケーションを他のシステム (レガシー システム、外部リソース、API など) と統合することを検討し、それに応じて計画する必要があります。このセッションでは、最新のシステムでレート制限が非常に重要である理由と、周囲のすべてのシステムの良き隣人として動作する優れたシステムを設計することが重要である理由を学びます。
セッション
膨大な量のデータを処理でき、大規模に動作するシステムを構築するには、どうすればよいのでしょうか。
この話を始める前に、モバイル・アプリケーションについて少し触れておきましょう。当初は数百人のユーザーを対象としたシステムを開発していましたが、1000人と増えていき、現在では数百万人規模のシステムを構築する巨大なシステムになりました。数百万人規模のシステムをどのように構築するかなど、グローバル化に関連するあらゆる課題を考えるようになりました。 私たちの顧客はどこにでもいて、さまざまな場所からシステムにアクセスしています。これは非常に大きな課題です。グローバルなシステム構築には、技術的な課題が多く、複雑です。
レスポンスタイムは超重要です。人々は、自分が探しているものを本当に早く手に入れることに慣れています。 データはもちろん、最近のアプリケーションでは非常に大きなデータとなっており、ペタバイトデータになるとギガバイトになり始めています。私たちは、このようなシステムで大量のデータを扱っています。
起動できる AWS Lambda の数は、基本的にアカウントの制限によって制限されます。アプリがFargate、またはSQSやDynamoDBのような非常に高いスケールのために設計されていれば問題ないはずです。 外部システムなどのレガシーシステムで、それについて何も知らない場合や、サードパーティのAPI、あるいはリレーショナルデータベースで作業している場合、このことを考慮に入れていないと、これからお話しするようなあらゆる種類の問題にぶつかる可能性があります。
スロットリングからブロッキングまで、APIに関するあらゆる問題にぶつかる可能性があります。相手側のAPIルールを尊重していないと、スロットルされたりブロックされたり、タイムアウトに陥ったりする可能性があります。 そこで、相手側でシステムが利用できなくなったとき。うまくいけば、タイムアウトを設定しても、アプリケーションがタイムアウトして再試行するので、ダウンタイムが発生する可能性があります。
AWS Lambda を実行したときに、相手側からの応答がなく、何度もリトライすると、その分コストがかかるということを考えるべきでしょう。
顧客体験に確実に影響を及ぼし、DDoS攻撃にもつながるので考慮に入れておく必要があります。
では、どうすればいいのでしょうか?どうすればそれを防げるのでしょうか? 私のアプリケーションが良き隣人であることを確認するにはどうしたらよいでしようか? そのシンプルな答えは、基本的に「制限」です。
(聴衆参加型のデモ)
デモアプリケーションは非常にシンプルで、新しいデータベースを書き込む以外は基本的に何もしないのです。同時に実行する AWS Lambda が多ければ多いほど、システム全体にさらなる負荷がかかるので、そうでなければ AWS Lambda の多くを再利用することになります。 このように、DynamoDBにどんどん結果を追加しています。今は何の制限もありませんが、いくつかの制限を導入したいと思います。
DynamoDBはスケーラブルではありませんが、非常に高価で、非常に高速です。 だから、制限を加え実際に何をするのかコントロールできるようにしたいのです。
では、制限について少しお話ししましょう。
最も基本的なことは、同時実行数を設定することです。コンソールで設定したり、コマンドラインで設定したり、設定した数以上にラムダ関数が実行されないようにすることができます。重要なのは、同時実行は本当に強力で、リージョンして、すべてのアカウントに制限があることです。デフォルトで1000という制限がありますが、この制限をサポートでこの制限を増やすことができます。
リレーショナル・データベースについて少し話しましょう。多くのアプリケーションがリレーショナルデータベースを使用しています。リレーショナル・データベースの問題点の一つは、アプリケーションの設定を正しく行わないと、コネクションから外れるという問題に直面することです。
DynamoDB から RDS に切り替えてみると、ほとんどの方が2~3回クリックしただけでエラーになったり、本当にあっという間に終わってしまうと思います。その理由は、AWS Lambda が接続を再利用しないからです。
では、どうすればいいのでしょうか。 まず第一にバッファを使うことがです。 SQSに何をすべきかを伝えるだけで、他のプロセス、つまりキューからこの情報を受け取り、どのレートでそれをどうするかを決める関数があるということです。
RDS Proxy は比較的新しいサービスで、アプリケーションとリレーショナル・データベースの間に置かれます。 RDS Proxy は、アプリケーションとデータベース間のすべての接続を管理し、それらを利用することで、アプリケーションがすべての接続を生じさせないようにします。
データの集約やより高度な機能を実現するために、DynamoDB から RDS に移行する必要があります。これは非常に難しいことで、データストアを変更する必要があるため、スケーラビリティの低いデータストアを使用することになります。
先ほど見たように、問題のひとつは接続プールの管理です。そこで、この状況に対処するために、アーキテクチャの別の部分を追加する必要があります。
AWS の API Gateway を使っている場合はスロットリングを設定することができる。そしてこれらによって、レガシーアプリケーションを保護することができる。
もう一つの素晴らしい方法は、キャッシュを使うことです。キャッシュはまさに、レガシー・アプリケーションにデータを取り込みに行く回数を減らすという考え方です。キャッシュを行う方法はいろいろありますが、データをミリ秒単位で更新する必要がない場合は、多くのシナリオが考えられます。たとえ10回に1回しか取得しなくても、90%のトラフィックを90%の割合で節約することができます。そのためにいくつかのオプションがあります。
そして次にお話しするのは、アプリケーションの設計方法を見直すということです。
まず、SNSとSQSです。 SQSは十分なバッファを提供してくれますし、デジタルキューやリトライメカニズムなど、さまざまな機能を提供してくれます。
多くのワークロードを実行する時、 AWS Step Functions が選択肢になります。
各ステップにはデフォルトのエラーハンドリングがあります。
大規模なスケーラブル・システムを構築する場合、tryやcatchを内部で実行すると、非常に複雑になります。そこで、この複雑さをコードの外に引き出そうとするのが、まさにこの機能です。コードの外側にあるステップ間の状態を管理する複雑さはありませんから、非常にスケーラブルに可視化することができます。
所感
規模が大きくなるにつれて、現状の設計のよっては思わぬところでDDoSになりかねないことが伝えたれていました。備わっている機能やサービスを駆使して規模が大きくなっても相手側に影響を与えない方法をしっかり学べました。